package mm.test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import mm.util.StringUtils;
import mm.util.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class MockContextImpl extends MockContextAdapter implements MockContext {

	private Map<String, Filter> filterMap=new HashMap<String, Filter>();
	private Map<String, String> initParameterMap=new HashMap<String, String>();
	private Map<String, Object> attributes=new HashMap<String, Object>();

	public MockContextImpl(String webPath) throws Exception{
		String webXmlPath=webPath+"/WEB-INF/web.xml";
		Document document=XMLUtils.readFilePath(webXmlPath, false);
		NodeList paramNodes = document.getDocumentElement().getElementsByTagName("context-param");
		for (int i=0;i<paramNodes.getLength();i++) {
			Element paramElement = (Element)paramNodes.item(i);
			String key=((Element)paramElement.getElementsByTagName("param-name").item(0)).getTextContent();
			String value=((Element)paramElement.getElementsByTagName("param-value").item(0)).getTextContent();
			initParameterMap.put(key, value);
		}
		NodeList listenerNodes = document.getDocumentElement().getElementsByTagName("listener");
		for (int i=0;i<listenerNodes.getLength();i++) {
			Element listenerElement = (Element)listenerNodes.item(i);
			String className=((Element)listenerElement.getElementsByTagName("listener-class").item(0)).getTextContent();
			Class clazz=Class.forName(className);
			if(Arrays.asList(clazz.getInterfaces()).contains(ServletContextListener.class)){
				clazz.getConstructor(new Class[]{String.class}).newInstance(new Object[]{null});
			}
		}
		NodeList filterNodes = document.getDocumentElement().getElementsByTagName("filter");
		NodeList filterMappingNodes = document.getDocumentElement().getElementsByTagName("filter-mapping");
		for (int i=0;i<filterNodes.getLength();i++) {
			Element filterElement = (Element)filterNodes.item(i);
			String filterName=((Element)filterElement.getElementsByTagName("filter-name").item(0)).getTextContent();
			String className=((Element)filterElement.getElementsByTagName("filter-class").item(0)).getTextContent();
			Class clazz=Class.forName(className);
			Filter filter=(Filter)clazz.newInstance();
			filter.init(new MockFilterConfig(this, filterName));
			for (int j=0;j<filterMappingNodes.getLength();j++) {
				Element filterMappingElement = (Element)filterMappingNodes.item(i);
				String _filterName=((Element)filterMappingElement.getElementsByTagName("filter-name").item(0)).getTextContent();
				if(filterName.equals(_filterName)){
					String urlPattern=((Element)filterMappingElement.getElementsByTagName("url-pattern").item(0)).getTextContent();
					filterMap.put(urlPattern, filter);
					break;
				}
			}
		}
	}
	
	public MockSession createSession(){
		return new MockSessionImpl(this);
	}

	public MockResponse accept(MockRequest mockRequest) {
		String path = ((HttpServletRequest)mockRequest).getServletPath();
		List<Filter> filters=new ArrayList<Filter>();
		for(String urlPattern : filterMap.keySet()){
			if(StringUtils.matchWildcard(path, urlPattern)){
				filters.add(filterMap.get(urlPattern));
			}
		}
		List<String> filterTour=new ArrayList<String>();
		MockResponse mockResponse=new MockResponseImpl((HttpServletRequest)mockRequest,filterTour);
		MockChain mockChain=new MockChainImpl(filters);
		try {
			((FilterChain)mockChain).doFilter((HttpServletRequest)mockRequest, (HttpServletResponse)mockResponse);
		} catch (IOException e) {
			e.printStackTrace(); // impossible
		} catch (ServletException e) {
			e.printStackTrace(); // impossible
		}
		return mockResponse;
	}

	@Deprecated
	@Override
	public String getContextPath() {
		return "/abc";
	}

	@Override
	public String getInitParameter(String key) {
		return initParameterMap.get(key);
	}

	@Override
	public Object getAttribute(String arg0) {
		return attributes.get(arg0);
	}

	@Override
	public void setAttribute(String arg0, Object arg1) {
		attributes.put(arg0, arg1);
	}

}
